package com.evil.enterprise.interseptor;

import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.http.Header;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.evil.account.api.RemoteLoginService;
import com.evil.common.account.dto.user.UserBaseRespDTO;
import com.evil.common.core.util.ServletUtils;
import com.evil.common.log.annotation.RequestLogAnnotation;
import com.evil.common.log.constant.LogConstant;
import com.evil.common.log.util.LogUtil;
import com.evil.log.api.RemoteOperationLogService;
import com.evil.log.api.RemoteRequestLogService;
import com.evil.log.pojo.dto.RequestLogDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.InputStreamSource;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.WebRequest;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.InputStream;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;


/**
 * 操作日志日志
 *
 * @author liyang
 * @date 2022-08-08 00:00
 */
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class RequestLogInterceptor {

    @Value("${spring.application.name}")
    private String applicationName;

    @Value("${spring.profiles.active}")
    private String environment;

    private final RemoteLoginService loginService;

    private final RemoteRequestLogService requestLogService;

    private final RemoteOperationLogService remoteOperationLogService;

    private final Logger requestLogger = LoggerFactory.getLogger(LogConstant.WEB_REQUEST_LOGGER);

    @Pointcut("execution(public * * (..) ) && @annotation(com.evil.common.log.annotation.RequestLogAnnotation)")
    public void logPointCut() {
    }

    @Before("logPointCut() && @annotation(requestLogAnnotation)")
    public void access(JoinPoint joinPoint, RequestLogAnnotation requestLogAnnotation) {
        UserBaseRespDTO loginUser = loginService.getLoginUser();

        RequestLogDTO requestLog = new RequestLogDTO();
        try {
            InetAddress localHost = InetAddress.getLocalHost();
            requestLog.setServerIp(ObjectUtils.isNotEmpty(localHost) ? localHost.getHostAddress() : "");
            requestLog.setServerHost(ObjectUtils.isNotEmpty(localHost) ? localHost.getHostName() : "");
            requestLog.setServiceId(applicationName);
            requestLog.setEnv(environment);
        } catch (Exception addressException) {
            log.error(addressException.getMessage(), addressException);
        }

        HttpServletRequest request = ServletUtils.getRequest();

        requestLog.setRequestMethod(request.getMethod());

        String requestParams = null;
        String requestParamsFull = null;
        String requestBody = null;
        String requestBodyFull = null;

        try {
            requestParamsFull = JSON.toJSONString(ServletUtil.getParamMap(request));
            requestParams = LogUtil.interceptContent(requestParamsFull);

            Object[] args = joinPoint.getArgs();
            List<?> paramList = new ArrayList<>(Arrays.asList(args))
                    .stream().filter(p -> !(p instanceof ServletResponse
                            || p instanceof ServletRequest || p instanceof WebRequest || p instanceof InputStream
                            || p instanceof InputStreamSource))
                    .collect(Collectors.toList());
            requestBodyFull = JSON.toJSONString(paramList);
            requestBody = LogUtil.interceptContent(requestBodyFull);
        } catch (Exception e) {
            log.error("【请求日志】提取请求参数错误: ", e);
        }

        if (requestLogAnnotation.isSaveRequestData()) {
            requestLog.setRequestParams(requestParams);
            requestLog.setRequestBody(requestBody);
        }

        requestLog.setRequestUri(request.getRequestURI());
        requestLog.setRequestUrl(request.getRequestURL().toString());
        requestLog.setClientIp(ServletUtil.getClientIP(request));
        requestLog.setUserAgent(request.getHeader(Header.USER_AGENT.toString()));

        requestLog.setTime(new Date());

        requestLog.setCreatorId(loginUser.getUserId());
        requestLog.setCreatorName(loginUser.getNickName());

        requestLog.setCreated(new Date());

        // 普通日志
        requestLog = requestLogService.insertRequestLog(requestLog);

        // 文件日志需要记录请求内容
        requestLog.setRequestParams(requestParamsFull);
        requestLog.setRequestBody(requestBodyFull);
        requestLogger.info(JSON.toJSONString(requestLog));

        request.setAttribute(LogConstant.OPERATE_LOG_HEADER, requestLog.getRequestLogId());
    }

    @AfterReturning(value = "logPointCut()")
    public void doAfterReturning(JoinPoint joinPoint) {
        remoteOperationLogService.insertOperationLog();
    }
}
